package org.adrianwalker;

import java.io.IOException;
import java.io.OutputStream;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.StandardOpenOption.READ;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/stream")
public final class PseudoStreamingServlet extends HttpServlet {

	private static final int BUFFER_LENGTH = 1024 * 16; // 缓冲区大小
	private static final long EXPIRE_TIME = 1000 * 60 * 60 * 24; // 过期时间
	private static final Pattern RANGE_PATTERN = Pattern.compile("bytes=(?<start>\\d*)-(?<end>\\d*)"); // 正则表达式匹配请求头中的range
	private String videoPath; // 视频文件路径

	@Override
	public void init() throws ServletException {
		videoPath = "D:\\Users\\Desktop\\video2servlet\\src\\main\\webapp\\Videos"; // 初始化视频文件路径
	}

	@Override
	protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
			throws ServletException, IOException {
		processRequest(request, response); // 处理请求
	}

	private void processRequest(final HttpServletRequest request, final HttpServletResponse response)
			throws IOException {
		
		String videoFilename = URLDecoder.decode(request.getParameter("video"), "UTF-8"); // 获取请求参数中的视频文件名
		
		Path video = Paths.get(videoPath, videoFilename); // 获取视频文件路径

		int length = (int) Files.size(video); // 获取视频文件大小
		int start = 0; // 起始位置
		int end = length - 1; // 结束位置

		String range = request.getHeader("Range"); // 获取请求头中的range
		Matcher matcher = RANGE_PATTERN.matcher(range); // 匹配range

		if (matcher.matches()) { // 如果匹配成功
			String startGroup = matcher.group("start"); // 获取起始位置
			start = startGroup.isEmpty() ? start : Integer.parseInt(startGroup); // 如果起始位置为空，则使用默认值0，否则使用请求头中的值
			start = Math.max(start, 0); // 如果起始位置小于0，则使用默认值0

			String endGroup = matcher.group("end"); // 获取结束位置
			end = endGroup.isEmpty() ? end : Integer.parseInt(endGroup); // 如果结束位置为空，则使用默认值length-1，否则使用请求头中的值
			end = Math.min(end, length - 1); // 如果结束位置大于文件大小，则使用默认值length-1
		}

		int contentLength = end - start + 1; // 内容长度

		response.reset(); // 重置响应
		response.setBufferSize(BUFFER_LENGTH); // 设置缓冲区大小
		response.setHeader("Content-Disposition", String.format("inline;filename=\"%s\"", videoFilename)); // 设置响应头中的Content-Disposition
		response.setHeader("Accept-Ranges", "bytes"); // 设置响应头中的Accept-Ranges
		response.setDateHeader("Last-Modified", Files.getLastModifiedTime(video).toMillis()); // 设置响应头中的Last-Modified
		response.setDateHeader("Expires", System.currentTimeMillis() + EXPIRE_TIME); // 设置响应头中的Expires
		response.setContentType(Files.probeContentType(video)); // 设置响应类型
		response.setHeader("Content-Range", String.format("bytes %s-%s/%s", start, end, length)); // 设置响应头中的Content-Range
		response.setHeader("Content-Length", String.format("%s", contentLength)); // 设置响应头中的Content-Length
		response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); // 设置响应状态为206

		int bytesRead;
		int bytesLeft = contentLength;
		ByteBuffer buffer = ByteBuffer.allocate(BUFFER_LENGTH);

		try (SeekableByteChannel input = Files.newByteChannel(video, READ);
				OutputStream output = response.getOutputStream()) {

			input.position(start);

			while ((bytesRead = input.read(buffer)) != -1 && bytesLeft > 0) { // 读取视频文件
				buffer.clear();
				output.write(buffer.array(), 0, Math.min(bytesLeft, bytesRead)); // 写入响应
				bytesLeft -= bytesRead;
			}
		}

	}
}
